//
// Created by 张易生 on 2022/1/12.
//

#include "ZYSAudioPlayer.h"


/**
 * @param playstatus 播放器状态
 * @param sample_rate 采样率
 */
ZYSAudioPlayer::ZYSAudioPlayer(ZYSPlayStatus *playstatus, int sample_rate, ZYSCallJava *callJava) {
    this->playStatus = playstatus;
    this->sample_rate = sample_rate;
    this->callJava = callJava;
    queue = new ZYSQueue(playstatus);
    buffer = (uint8_t *) av_malloc(sample_rate * 2 * 2);
    sampleBuffer = static_cast<SAMPLETYPE *>(malloc(sample_rate * 2 * 2));
    soundTouch = new SoundTouch();
    soundTouch->setChannels(2);
    soundTouch->setSampleRate(sample_rate);
    soundTouch->setPitch(pitch);
    soundTouch->setTempo(speed);
}

//c函数 需要转换到c++函数调用
void *decodePlay(void *data) {
    ZYSAudioPlayer *zysAudio = (ZYSAudioPlayer *) data;

    zysAudio->initOpenSLES();

    pthread_exit(&zysAudio->thread_play);
}


void ZYSAudioPlayer::play() {
    pthread_create(&thread_play, nullptr, decodePlay, this);
}

//初始化完opensles后， 需要播放数据时会自动回调此函数，索要数据
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void *context) {
//    LOGE("-------->pcmBufferCallBack ");
    ZYSAudioPlayer *zysAudio = (ZYSAudioPlayer *) context;
    if (zysAudio != nullptr) {
        zysAudio->playPCM();
    }
}

//播放
void ZYSAudioPlayer::playPCM() {
    if (playStatus->exit) {
        return;
    }
    if (speed == 1.0f && pitch == 1.0f) {
        int bufferSize = resampleAudio(
                reinterpret_cast<void **>(&out_buffer));

        if (bufferSize > 0) {
            //得到当前buffer播放需要多少时间
            clock += bufferSize / ((double) (sample_rate * 2 * 2));
            if (clock - last_time >= 0.5) {
                last_time = clock;
                //pcmBufferCallBack 回调次数比较频繁 0.9秒回调一次java
                callJava->onCallTimeInfo(CHILD_THREAD, clock,
                                         duration);
            }
            if (pcmBufferQueue == nullptr) {
                return;
            }
            (*pcmBufferQueue)->Enqueue(pcmBufferQueue, (char *) buffer, bufferSize);
        }
    } else {
        int bufferSize = getSoundTouchSampleData();

        if (bufferSize > 0) {
            //得到当前buffer播放需要多少时间
            clock += bufferSize / ((double) (sample_rate * 2 * 2));
            //pcmBufferCallBack 回调次数比较频繁 0.9秒回调一次java
            if (clock - last_time >= 0.5) {
                last_time = clock;
                callJava->onCallTimeInfo(CHILD_THREAD, clock, duration);
            }
            if (pcmBufferQueue == nullptr) {
                return;
            }
            (*pcmBufferQueue)->Enqueue(pcmBufferQueue, (char *) sampleBuffer, bufferSize * 2 * 2);
        }
    }
}

//初始化Opensles
void ZYSAudioPlayer::initOpenSLES() {

    SLresult result;
    result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

    //第二步，创建混音器
    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};//环境回响
    //不开启环境回响
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
    (void) result;
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    (void) result;
    result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
                                              &outputMixEnvironmentalReverb);
    if (SL_RESULT_SUCCESS == result) {
        result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                outputMixEnvironmentalReverb, &reverbSettings);
        (void) result;
    }
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
    SLDataSink audioSnk = {&outputMix, 0};


    // 第三步，配置PCM格式信息
    SLDataLocator_AndroidSimpleBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                            2};
    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,//播放pcm格式的数据
            2,//2个声道（立体声）
            static_cast<SLuint32>(getCurrentSampleRateForOpensles(sample_rate)),//44100hz的频率
            SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
            SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声（前左前右）
            SL_BYTEORDER_LITTLEENDIAN//结束标志
    };
    SLDataSource slDataSource = {&android_queue, &pcm};


    //注册要用到的接口
    const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_MUTESOLO, SL_IID_VOLUME};
    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

    (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 3,
                                       ids, req);
    //初始化播放器
    (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);

//    得到接口后调用  获取Player播放和暂停接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
    //声道接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &playerMute);
    //声音大小接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &playerVolume);

//    注册回调缓冲区 获取缓冲队列接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
    //缓冲接口回调
    (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
//    获取播放状态接口
    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    pcmBufferCallBack(pcmBufferQueue, this);

}


int ZYSAudioPlayer::getCurrentSampleRateForOpensles(int sample_rate) {
    int rate = 0;
    switch (sample_rate) {
        case 8000:
            rate = SL_SAMPLINGRATE_8;
            break;
        case 11025:
            rate = SL_SAMPLINGRATE_11_025;
            break;
        case 12000:
            rate = SL_SAMPLINGRATE_12;
            break;
        case 16000:
            rate = SL_SAMPLINGRATE_16;
            break;
        case 22050:
            rate = SL_SAMPLINGRATE_22_05;
            break;
        case 24000:
            rate = SL_SAMPLINGRATE_24;
            break;
        case 32000:
            rate = SL_SAMPLINGRATE_32;
            break;
        case 44100:
            rate = SL_SAMPLINGRATE_44_1;
            break;
        case 48000:
            rate = SL_SAMPLINGRATE_48;
            break;
        case 64000:
            rate = SL_SAMPLINGRATE_64;
            break;
        case 88200:
            rate = SL_SAMPLINGRATE_88_2;
            break;
        case 96000:
            rate = SL_SAMPLINGRATE_96;
            break;
        case 192000:
            rate = SL_SAMPLINGRATE_192;
            break;
        default:
            rate = SL_SAMPLINGRATE_44_1;
    }
    return rate;
}


int ZYSAudioPlayer::resampleAudio(void **pcm_buffer) {
    while (playStatus != nullptr && !playStatus->exit) {
        avPacket = av_packet_alloc();
        if (queue->getAVPacket(avPacket) != 0) {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = nullptr;
            continue;
        }
        ret = avcodec_send_packet(avCodecContext, avPacket);
        if (ret != 0) {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = nullptr;
            continue;
        }
        avFrame = av_frame_alloc();
        ret = avcodec_receive_frame(avCodecContext, avFrame);
        if (ret == 0) {

            if (avFrame->channels && avFrame->channel_layout == 0) {
                avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);
            } else if (avFrame->channels == 0 && avFrame->channel_layout > 0) {
                avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);
//                LOGE("avFrame->channels= %d", avFrame->channels)
            }
//            LOGE("avFrame->sample_rate= %d", avFrame->sample_rate)

            //转换器，重新采样
            SwrContext *swr_ctx;

            swr_ctx = swr_alloc_set_opts(
                    NULL,
                    AV_CH_LAYOUT_STEREO,
                    AV_SAMPLE_FMT_S16,
                    avFrame->sample_rate,
                    avFrame->channel_layout,
                    (AVSampleFormat) avFrame->format,
                    avFrame->sample_rate,
                    NULL, NULL
            );
            if (!swr_ctx || swr_init(swr_ctx) < 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                swr_free(&swr_ctx);
                continue;
            }

            //将avFrame->data进行重采样，之后将数据放入buffer中
            buffer_number = swr_convert(
                    swr_ctx,
                    &buffer,
                    avFrame->nb_samples,
                    (const uint8_t **) avFrame->data,
                    avFrame->nb_samples);

            int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
            data_size = buffer_number * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
            //得到当前Frame的播放时间
            // 这句等同于下面 now_time=  avFrame->pts *  time_base.num / (double) time_base.den;
            now_time = avFrame->pts * av_q2d(time_base);
            if (now_time < clock) {
                now_time = clock;
            }
            //实际的时间
            clock = now_time;
            *pcm_buffer = buffer;
            if (clock >= duration) {
                stop();
            }
            if (LOG_DEBUG) {
//                LOGE("data_size is %d", data_size);
            }
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            swr_free(&swr_ctx);
            break;
        } else {
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            continue;
        }
    }
    return data_size;

}

int ZYSAudioPlayer::getSoundTouchSampleData() {


    while (playStatus != nullptr && !playStatus->exit) {
        if (soundTouch == nullptr) {
            break;
        }
        out_buffer = nullptr;
        if (soundTouchFinish) {
            soundTouchFinish = false;
            data_size = this->resampleAudio(reinterpret_cast<void **>(&out_buffer));
            if (data_size > 0) {
                for (int i = 0; i < data_size / 2 + 1; i++) {
                    //short2个字节
                    sampleBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
                }
                //丢给sountouch进行波的整理,buffer_number:这是几个buffer
                soundTouch->putSamples(sampleBuffer, buffer_number);
//                接受一个新波 sampleBuffer
                newPcmNum = soundTouch->receiveSamples(sampleBuffer, data_size / 4);
            } else {
                soundTouch->flush();
            }
        }
        if (newPcmNum == 0) {
            soundTouchFinish = true;
            continue;
        } else {

            if (out_buffer == nullptr) {
                if (soundTouch == nullptr) {
                    break;
                }
                newPcmNum = soundTouch->receiveSamples(sampleBuffer, data_size / 4);
                if (newPcmNum == 0) {
                    soundTouchFinish = true;
                    continue;
                }
            }
            return newPcmNum;
        }
    }
    return 0;


}


void ZYSAudioPlayer::pause() {
    if (pcmPlayerPlay != nullptr) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
    }
}

void ZYSAudioPlayer::resume() {
    if (pcmPlayerPlay != nullptr) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
    }
}

void ZYSAudioPlayer::stop() {
    if (pcmPlayerPlay != nullptr) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
    }
}

void ZYSAudioPlayer::setMute(int mute) {
    if (playerMute == nullptr) {
        return;
    }
    this->mute = mute;
    if (mute == 0) {//right   0
        (*playerMute)->SetChannelMute(playerMute, 1, false);
        (*playerMute)->SetChannelMute(playerMute, 0, true);

    } else if (mute == 1) {//left
        (*playerMute)->SetChannelMute(playerMute, 1, true);
        (*playerMute)->SetChannelMute(playerMute, 0, false);
    } else if (mute == 2) {//center

        (*playerMute)->SetChannelMute(playerMute, 1, false);
        (*playerMute)->SetChannelMute(playerMute, 0, false);
    }
}

void ZYSAudioPlayer::setVolume(int percent) {
    if (playerVolume != NULL) {
        if (percent > 30) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -20);
        } else if (percent > 25) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -22);
        } else if (percent > 20) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -25);
        } else if (percent > 15) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -28);
        } else if (percent > 10) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -30);
        } else if (percent > 5) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -34);
        } else if (percent > 3) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -37);
        } else if (percent > 0) {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -40);
        } else {
            (*playerVolume)->SetVolumeLevel(playerVolume, (100 - percent) * -100);
        }
    }
}

void ZYSAudioPlayer::setSpeed(float speed) {
    this->speed = speed;
    LOGE("setSpeed--speed--- %f", speed)
    if (soundTouch != nullptr) {
        soundTouch->setTempo(speed);
    }
}

void ZYSAudioPlayer::setPitch(float pitch) {
    this->pitch = pitch;
    LOGE("setPitch--pitch--- %f", pitch)
    if (soundTouch != nullptr) {
        soundTouch->setPitch(pitch);
    }
}

void ZYSAudioPlayer::release() {
    if (playStatus != nullptr) {
        playStatus->exit = true;
    }
    if (pcmPlayerPlay != nullptr) {
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
    }
    if (queue != nullptr) {
        queue->clearAVPacket();
        delete (queue);
        queue = nullptr;
    }

    if (pcmPlayerObject != nullptr) {
        (*pcmPlayerObject)->Destroy(pcmPlayerObject);
        pcmPlayerObject = nullptr;
        pcmPlayerPlay = nullptr;
        playerMute = nullptr;
        playerVolume = nullptr;
        pcmBufferQueue = nullptr;
    }
    if (outputMixObject != nullptr) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = nullptr;
        outputMixEnvironmentalReverb = nullptr;
    }
    if (engineObject != nullptr) {
        (*engineObject)->Destroy(engineObject);
        engineObject = nullptr;
        engineEngine = nullptr;
    }
    if (buffer != nullptr) {
        free(buffer);
        buffer = nullptr;
    }
    if (avCodecContext != nullptr) {
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = nullptr;
    }
    if (soundTouch != nullptr) {
        delete (soundTouch);
        soundTouch = nullptr;
    }
    if (callJava != nullptr) {
        delete (callJava);
        callJava = nullptr;
    }
    if (playStatus != nullptr) {
        delete (playStatus);
        playStatus = nullptr;
    }
}

ZYSAudioPlayer::~ZYSAudioPlayer() {

}










